package com.oxymore.jbnetapi.connectors;

import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import com.oxymore.jbnetapi.core.ApiException;
import com.oxymore.jbnetapi.core.ApiPage;
import com.oxymore.jbnetapi.core.ApiPath;
import com.oxymore.jbnetapi.core.ApiRegion;
import com.oxymore.jbnetapi.core.ApiRequest;
import com.oxymore.jbnetapi.wow.auction.AuctionDump;
import com.oxymore.jbnetapi.wow.character.Profile;
import com.oxymore.jbnetapi.wow.guild.Guild;
import com.oxymore.jbnetapi.wow.item.Item;
import com.oxymore.jbnetapi.wow.pvp.Arena;
import com.oxymore.jbnetapi.wow.realm.Realms;

/**
 * The World of Warcraft API connector, it's used to query the Bnet WoW API, see
 * further documentation.
 * 
 * @author Valentin
 * 
 */
public final class WoWApiConnector extends AbstractConnector {

    private static WoWApiConnector instance;

    /**
     * 
     * @param regionBaseUrl
     *            the base url of your bnet's region {@link ApiRegion}
     * @param privateKey
     *            your API private key, given by Blizzard
     * @param publicKey
     *            your API public key, given by Blizzard
     * @param defaultLocale
     *            the default locale you wish to set up for this connector,
     *            every subsequent query will be done with this locale unless
     *            you specify another one
     * @return
     */
    public static WoWApiConnector getInstance(String regionBaseUrl,
            String privateKey, String publicKey, String defaultLocale) {
        if (instance == null) {
            instance = new WoWApiConnector(regionBaseUrl, privateKey,
                    publicKey, defaultLocale);
        }
        if (privateKey == null || publicKey == null) {
            instance.usingAuth = false;
        } else {
            instance.usingAuth = true;
        }
        return instance;
    }

    /**
     * 
     * @param regionBaseUrl
     *            the base url of your bnet's region {@link ApiRegion}
     * @param defaultLocale
     *            the default locale you wish to set up for this connector,
     *            every subsequent query will be done with this locale unless
     *            you specify another one
     * @return
     */
    public static WoWApiConnector getInstance(String regionBaseUrl,
            String defaultLocale) {
        return getInstance(regionBaseUrl, null, null, defaultLocale);
    }

    /**
     * 
     * @param regionBaseUrl
     *            the base url of your bnet's region {@link ApiRegion}
     * @return
     */
    public static WoWApiConnector getInstance(String regionBaseUrl) {
        String defaultLocale = null;
        switch (regionBaseUrl) {
            case ApiRegion.EU:
                defaultLocale = "en_GB";
                break;
            case ApiRegion.US:
                defaultLocale = "en_US";
                break;
        }
        return getInstance(regionBaseUrl, null, null, defaultLocale);
    }

    /**
     * 
     * @param regionBaseUrl
     *            the base url of your bnet's region {@link ApiRegion}
     * @param privateKey
     *            your API private key, given by Blizzard
     * @param publicKey
     *            your API public key, given by Blizzard
     * @return
     */
    public static WoWApiConnector getInstance(String regionBaseUrl,
            String privateKey, String publicKey) {
        String defaultLocale = null;
        switch (regionBaseUrl) {
            case ApiRegion.EU:
                defaultLocale = "en_GB";
                break;
            case ApiRegion.US:
                defaultLocale = "en_US";
                break;
        }
        return getInstance(regionBaseUrl, privateKey, publicKey, defaultLocale);
    }

    /**
     * @param regionBaseUrl
     *            the base url of your bnet's region {@link ApiRegion}
     * @param privateKey
     *            your API private key, given by Blizzard
     * @param publicKey
     *            your API public key, given by Blizzard
     */
    private WoWApiConnector(String regionBaseUrl, String privateKey,
            String publicKey, String defaultLocale) {
        super(regionBaseUrl, privateKey, publicKey, defaultLocale);
    }

    /**
     * Return the status of all realms if no parameters are passed or the status
     * of the specified realm(s)
     * 
     * @param realmList
     *            a string representing a list of realms coma separated eg:
     *            "medivh,chogall"
     * @return a {@link Realms} instance containing the result of your request
     * @throws ApiException
     */
    public Realms getRealmsStatus(String realmList) throws ApiException {
        Map<String, String> params = new HashMap<>();
        params.put("realms", realmList);

        final ApiRequest request = new ApiRequest(ApiPath.WOW_REALM,
                ApiPage.WOW_REALM_STATUS, params);
        final URL requestUrl = getURL(request);
        final String authString = buildAuthorization(requestUrl.getPath());
        Realms result = gson.fromJson(getContent(requestUrl, authString),
                Realms.class);

        if (result == null) {
            throw new ApiException("Empty response", requestUrl.toString());
        }
        if (result.getStatus() != null && result.getStatus().equals("nok")) {
            throw new ApiException(result.getStatus(), result.getReason(),
                    requestUrl.toString());
        }
        return result;
    }

    /**
     * @see WoWApiConnector#getChararacterProfile(String, String, String,
     *      String)
     * @param realmSlug
     *            the slug of the targeted character realm
     * @param charName
     *            the target character name
     * @param optionnalFields
     *            a string representing the list of additionnal information you
     *            wish to get for the target character
     * @return a {@link Profile} instance containing the result of your request
     * @throws ApiException
     */
    public Profile getChararacterProfile(String realmSlug, String charName,
            String optionnalFields) throws ApiException {
        return getChararacterProfile(realmSlug, charName, optionnalFields,
                defaultLocale);
    }

    /**
     * Retrieve base information plus specified information on a character
     * 
     * @param realmSlug
     *            the slug of the targeted character realm
     * @param charName
     *            the target character name
     * @param optionnalFields
     *            A coma separated string representing the list of additionnal
     *            information you wish to get for the target character. coma
     *            separated
     * @param locale
     *            the locale you in witch you'd like to get the result
     * @return a {@link Profile} instance containing the result of your request
     * @throws ApiException
     */
    public Profile getChararacterProfile(String realmSlug, String charName,
            String optionnalFields, String locale) throws ApiException {
        Map<String, String> params = new HashMap<>();
        params.put("fields", optionnalFields);
        if (locale != null) {
            params.put("locale", locale);
        }
        final ApiRequest request = new ApiRequest(ApiPath.WOW_CHARACTER,
                ApiPage.NONE, "/" + realmSlug + "/" + charName, params);
        final URL requestUrl = getURL(request);
        final String authString = buildAuthorization(requestUrl.getPath());
        Profile result = gson.fromJson(getContent(requestUrl, authString),
                Profile.class);
        if (result == null) {
            throw new ApiException("Empty response", requestUrl.toString());
        }
        if (result.getStatus() != null && result.getStatus().equals("nok")) {
            throw new ApiException(result.getStatus(), result.getReason(),
                    requestUrl.toString());
        }
        return result;
    }

    /**
     * 
     * @param realmSlug
     *            the slug of the targeted arena team realm
     * @param teamSize
     *            the size of the targeted team @see {@link ApiArenaTeamSize}
     * @param teamName
     *            the name of the targeted team
     * @return a {@link Arena} instance containing the result of your request
     * @throws ApiException
     */
    public Arena getArenaTeamInfo(String realmSlug, String teamSize,
            String teamName) throws ApiException {
        return getArenaTeamInfo(realmSlug, teamSize, teamName, defaultLocale);
    }

    /**
     * 
     * @param realmSlug
     *            the slug of the targeted arena team realm
     * @param teamSize
     *            the size of the targeted team @see {@link ApiArenaTeamSize}
     * @param teamName
     *            the name of the targeted team
     * @param locale
     *            the locale you in witch you'd like to get the result
     * @returna {@link Arena} instance containing the result of your request
     * @throws ApiException
     */
    public Arena getArenaTeamInfo(String realmSlug, String teamSize,
            String teamName, String locale) throws ApiException {
        Map<String, String> params = new HashMap<>();
        if (locale != null) {
            params.put("locale", locale);
        }
        final ApiRequest request = new ApiRequest(ApiPath.WOW_ARENA,
                ApiPage.NONE, "/" + realmSlug + "/" + teamSize + "/" + teamName);
        final URL requestUrl = getURL(request);
        final String authString = buildAuthorization(requestUrl.getPath());
        Arena result = gson.fromJson(getContent(requestUrl, authString),
                Arena.class);
        if (result == null) {
            throw new ApiException("Empty response", requestUrl.toString());
        }
        if (result.getStatus() != null && result.getStatus().equals("nok")) {
            throw new ApiException(result.getStatus(), result.getReason(),
                    requestUrl.toString());
        }
        return result;
    }

    /**
     * 
     * @param itemId
     *            the id of the targeted item
     * @return a {@link Item} instance containing the result of your request
     * @throws ApiException
     */
    public Item getItemInfo(Integer itemId) throws ApiException {
        return getItemInfo(itemId, defaultLocale);
    }

    /**
     * 
     * @param itemId
     *            the id of the targeted item
     * @param locale
     *            the locale you in witch you'd like to get the result
     * @return a {@link Item} instance containing the result of your request
     * @throws ApiException
     */
    public Item getItemInfo(Integer itemId, String locale) throws ApiException {
        Map<String, String> params = new HashMap<>();
        if (locale != null) {
            params.put("locale", locale);
        }
        final ApiRequest request = new ApiRequest(ApiPath.WOW_ITEM,
                ApiPage.NONE, "/" + itemId.toString(), params);
        final URL requestUrl = getURL(request);
        final String authString = buildAuthorization(requestUrl.getPath());
        Item result = gson.fromJson(getContent(requestUrl, authString),
                Item.class);
        if (result == null) {
            throw new ApiException("", requestUrl.toString());
        }
        if (result.getStatus() != null && result.getStatus().equals("nok")) {
            throw new ApiException(result.getStatus(), result.getReason(),
                    requestUrl.toString());
        }
        return result;
    }

    /**
     * @see WoWApiConnector#getGuildProfile(String, String, String, String)
     * @param realmSlug
     *            the slug of the targeted guild's realm
     * @param guildName
     *            the name of the targeted guild
     * @param optionnalFields
     *            a string representing the list of additionnal information you
     *            wish to get on the targeted guild, coma separated
     * @return a {@link Guild} instance containing the result of your request
     * @throws ApiException
     */
    public Guild getGuildProfile(String realmSlug, String guildName,
            String optionnalFields) throws ApiException {
        return getGuildProfile(realmSlug, guildName, optionnalFields,
                defaultLocale);
    }

    /**
     * Retrieve basic and user specified information on a guild
     * 
     * @param realmSlug
     *            the slug of the targeted guild's realm
     * @param guildName
     *            the name of the targeted guild
     * @param optionnalFields
     *            a string representing the list of additionnal information you
     *            wish to get on the targeted guild, coma separated
     * @param locale
     *            the locale you in witch you'd like to get the result
     * @return a {@link Guild} instance containing the result of your request
     * @throws ApiException
     */
    public Guild getGuildProfile(String realmSlug, String guildName,
            String optionnalFields, String locale) throws ApiException {
        Map<String, String> params = new HashMap<>();
        params.put("fields", optionnalFields);
        if (locale != null) {
            params.put("locale", locale);
        }
        final ApiRequest request = new ApiRequest(ApiPath.WOW_GUILD,
                ApiPage.NONE, "/" + realmSlug + "/" + guildName, params);
        final URL requestUrl = getURL(request);
        final String authString = buildAuthorization(requestUrl.getPath());
        Guild result = gson.fromJson(getContent(requestUrl, authString),
                Guild.class);
        if (result == null) {
            throw new ApiException("Empty response", requestUrl.toString());
        }
        if (result.getStatus() != null && result.getStatus().equals("nok")) {
            throw new ApiException(result.getStatus(), result.getReason(),
                    requestUrl.toString());
        }
        return result;
    }

    /**
     * Retrieve the auctiondump of a specified realm, contain all the current
     * auction in the three auction houses on the realm
     * 
     * @param realmSlug
     *            the slug of the targeted realm
     * @return a {@link AuctionDump} instance containing the result of your
     *         request
     * @throws Exception
     */
    public AuctionDump getRealmAuctionDump(String realmSlug)
            throws ApiException {
        final ApiRequest request = new ApiRequest(ApiPath.WOW_AUCTION_DUMP,
                ApiPage.NONE, "/" + realmSlug + "/auctions.json");
        final URL requestUrl = getURL(request);
        final String authString = buildAuthorization(requestUrl.getPath());
        AuctionDump result = gson.fromJson(getContent(requestUrl, authString),
                AuctionDump.class);
        if (result == null) {
            throw new ApiException("Empty response", requestUrl.toString());
        }
        if (result.getStatus() != null && result.getStatus().equals("nok")) {
            throw new ApiException(result.getStatus(), result.getReason(),
                    requestUrl.toString());
        }
        return result;
    }
}
